﻿//
// Created by hujin on 2020/10/6.
//

#ifndef C_MIPS_CMPLR_AST_STATEMENTS_H
#define C_MIPS_CMPLR_AST_STATEMENTS_H

#include "ast.h"
#include "ast_exprs.h"

class StatNode : public  ASTNode {
public:
	StatNode() = default;
    virtual bool containsReturn(bool noVal, bool retInt) {return false;};
    virtual void genCode(Synthesizer *out, MemoryAllocator *allocator) {};

    virtual std::set<int> getUseVarIndex() {return {};}
};

//TODO: self add node
class AssignNode : public  StatNode {
	int indexExpTemp = 0;
public:
    IdentiferFactor* target;
    ExprNode* value;
	AssignNode(IdentiferFactor* target, ExprNode* value) : target(target), value(value) {}
	void genCode(Synthesizer *out, MemoryAllocator *allocator) override ;
	bool checkError(SymbolTable *table) override {
        if(target->getData().type == ID_CNST_C || target->getData().type == ID_CNST_I){
            logError("Assign to const.");
            log_error2('j', getLine());//不能改变常量的值
            return false;
        }
        return true;
    }


    void addToTable(SymbolTable* table) override{
        indexExpTemp = table->gettempVarID();
    }
};

class IfNode : public  StatNode {
	ConditionNode* condition;
	StatNode* ifStatement, * elseStatement = nullptr;
	int labelId; int labelIdEnd;
public:
	IfNode(ConditionNode* condition, StatNode* ifStatement) : condition(condition), ifStatement(ifStatement) {}

	IfNode(ConditionNode* condition, StatNode* ifStatement, StatNode* elseStatement) : condition(condition),
		ifStatement(ifStatement),
		elseStatement(elseStatement) {}

    bool containsReturn(bool noVal, bool retInt) override{return (ifStatement != nullptr && ifStatement->containsReturn(noVal, retInt))
             || (elseStatement != nullptr && elseStatement->containsReturn(noVal, retInt)) ;};


	void genCode(Synthesizer *out, MemoryAllocator *allocator) override;

    void addToTable(SymbolTable* table) override{
        labelId = table->getLabel();
        labelIdEnd = table->getLabel();
    }
};

class ConditionNode : public  ASTNode {
	ExprNode* left, * right;
	Symbol op;
	int tempIDLeft, temppIdRight;
public:
	ConditionNode(ExprNode* left, ExprNode* right, Symbol op) : left(left), right(right), op(op) {}

    bool checkError(SymbolTable* table) override ;

	MemoryAllocator * genCode(Synthesizer *out, MemoryAllocator *allocator, int labelId);

    void addToTable(SymbolTable* table) override{
        tempIDLeft = table->gettempVarID();
        temppIdRight = table->gettempVarID();
    }
};


class LoopNode : public  StatNode {
};

class WhileNode : public LoopNode {
	ConditionNode* condition;
	StatNode* statement;
	int labelR, labelEnd;
public:
	WhileNode(ConditionNode* condition, StatNode* statement) :
		LoopNode(), condition(condition), statement(statement) {}

    bool containsReturn(bool noVal, bool retInt) override{return statement != nullptr && statement->containsReturn(noVal, retInt);};


    void genCode(Synthesizer *out, MemoryAllocator *allocator) override;

    void addToTable(SymbolTable* table) override{
        labelR = table->getLabel();
        labelEnd = table->getLabel();
    }
};

class ForNode : public LoopNode {
	std::string initVar, alterVal1, alterVal2;
    Identifier_Data  initVarid = {}, alterVar1id = {}, alterVar2id = {};
	ExprNode* initialVal;
	ConditionNode* condition;
	StatNode* statements;
	int step;
	int labelR, labelEnd;
public:
	ForNode(std::string& initVar, std::string& alterVal1, std::string& alterVal2, ExprNode* initialVal,
		ConditionNode* condition, int step, StatNode* statNode) : LoopNode(), initVar(std::move(initVar)), alterVal1(std::move(alterVal1)), alterVal2(std::move(alterVal2)),
		initialVal(initialVal), condition(condition), step(step), statements(statNode) {}
    bool containsReturn(bool noVal, bool retInt) override{return statements != nullptr && statements->containsReturn(noVal, retInt);};
    void addToTable(SymbolTable* table) override{initVarid = table->getDiscriptions(initVar),
                alterVar1id = table->getDiscriptions(alterVal1),
                alterVar2id = table->getDiscriptions(alterVal2);

    labelEnd = table->getLabel();labelR = table->getLabel();};

    void genCode(Synthesizer *out, MemoryAllocator *allocator) override;
};

class SwitchTableNode : public  ASTNode {
    std::map<int, StatNode*> cases;
    std::vector<int> labels;
    int labelRet;
    StatNode* defaultStat;
public:
    SwitchTableNode(std::map<int, StatNode*> cases, StatNode* defaultStat) : cases(std::move(cases)),
                                                                             defaultStat(defaultStat) {};
    bool containsReturn(bool noVal, bool retInt);

    void genCode(Synthesizer *out, MemoryAllocator *allocator, int tempIndex);

    void addToTable(SymbolTable* table) override{
        labelRet = table->getLabel();
        for(int i = 0; i < cases.size(); i++){
            labels.push_back(table->getLabel());
        }
    }
};

class SwitchNode : public  StatNode {
	ExprNode* val;
	SwitchTableNode* switchTableNode;
	int tempIndex;
public:
	SwitchNode(ExprNode* val, SwitchTableNode* caseNode) : val(val), switchTableNode(caseNode) {}
    bool containsReturn(bool noVal, bool retInt) override{return switchTableNode->containsReturn(noVal, retInt);};

    void genCode(Synthesizer *out, MemoryAllocator *allocator) override;

    void addToTable(SymbolTable* table) override{
        tempIndex = table->gettempVarID();
    }
};


class FuncCallParamsNode : public  ASTNode{
    std::vector<ExprNode*> params;
    std::vector<int> tempIds;
public:
    const std::vector<ExprNode *> &getParams() const {
        return params;
    }

    void genCode(Synthesizer *out, MemoryAllocator *mem) ;

    void addToTable(SymbolTable* table) override{
        for(int i =0; i < params.size(); i++){
            tempIds.push_back(table->getVarID());
        }
    }

    std::set<int> getUseIds(){
        std::set<int> ret;
        for(auto x : params){
            const auto &p = x->getUseVarIndex();
            if(!p.empty()) ret.insert(p.begin(), p.end());
        }
        return ret;
    };

    explicit FuncCallParamsNode(std::vector<ExprNode*>& params) : params(std::move(params)) {}
};


class FuncCallNode : public StatNode {
protected:
	FuncCallParamsNode* params;
public:
    std::string name;
	FuncCallNode(std::string& name, FuncCallParamsNode* params) : name(std::move(name)), params(params) {}

    bool checkError(SymbolTable* table) override ;

	void genCode(Synthesizer *out, MemoryAllocator *mem) override;

	std::set<int> getUseVarIndex() override {return params->getUseIds();}

};

class VoidCallNode : public FuncCallNode {
public:
	VoidCallNode(std::string& name, FuncCallParamsNode* params) : FuncCallNode(name, params) {}
    bool checkError(SymbolTable* table) override ;

};

class StatListNode : public  StatNode {
	std::vector<StatNode*> statements;
public:
	explicit StatListNode(std::vector<StatNode*>& statements) : statements(std::move(statements)) {}

	bool containsReturn(bool noVal, bool retInt) override;

    void genCode(Synthesizer *out, MemoryAllocator *allocator) override;
};

class ScanfNode : public  StatNode {
	std::string id;
	Identifier_Data data;
public:
	explicit ScanfNode(std::string& id) : id(std::move(id)) {}

	bool checkError(SymbolTable *table) override{
        if(table->getDiscriptions(id).type == ID_CNST_C || table->getDiscriptions(id).type == ID_CNST_I){
            logError("Scanf writes to const.");
            log_error2('j', getLine());//不能改变常量的值
            return false;
        }
        return true;
	}

    void addToTable(SymbolTable* table) override{data = table->getDiscriptions(id);};


    void genCode(Synthesizer *out, MemoryAllocator *allocator) override ;
};

class PrintfNode : public  StatNode {

	std::string str;
	ExprNode* expr = nullptr;
	int var_tmp = 0;
public:

    void genCode(Synthesizer *out, MemoryAllocator *allocator) override ;
	explicit PrintfNode(std::string& str) : str(std::move(str)) {}
	explicit PrintfNode(ExprNode* expr) : expr(expr) {}
	PrintfNode(std::string& str, ExprNode* expr) : str(std::move(str)), expr(expr) {}

    void addToTable(SymbolTable *table) override{
        var_tmp = table->gettempVarID();
    }
};

class ReturnNode : public  StatNode {
	ExprNode* expr;//Nullable!
	int tempId = 0;
	bool insideMain;
public:
	ReturnNode(ExprNode *expr, bool isInMain) : expr(expr) , insideMain(isInMain){}

    bool containsReturn(bool noVal, bool retInt) override ;

    void addToTable(SymbolTable *table) override{
        if(expr != nullptr) tempId = table->gettempVarID();
    }
	void genCode(Synthesizer *out, MemoryAllocator *allocator) override;
};

class BadReturnNode : public ReturnNode{ // return ()
public:
    BadReturnNode() : ReturnNode(nullptr, false) {};

    bool containsReturn(bool noVal, bool retInt) override {return false;}
};

#endif //C_MIPS_CMPLR_AST_STATEMENTS_H
